#include "Logic.h"

char Logic::buffer[255];
const string Logic::WELCOME_MESSAGE_LOADED_EXISTING_FILE = "Welcome to TaskLine.\nloaded existing file";
const string Logic::WELCOME_MESSAGE_NEW_FILE = "Welcome to TaskLine.\ncreate a new file";
const string Logic::WELCOME_MESSAGE_FILE_ERROR = "cannot open a file";

Logic* Logic::theOnlyLogic = NULL;

Logic::Logic(){ 
	fileName = CommonConstant::EMPTY_STRING; 
	storage = Storage::getInstance();
}

Logic::~Logic(){}

Logic* Logic::getInstance()
{
	if(!theOnlyLogic){
		theOnlyLogic = new Logic();
	}
	return theOnlyLogic;
}

void Logic::initializeFileStream(string nameString){
	setFileName(nameString);
    loadFromFile();
	writeToFile();
}

void Logic::setFileName(string newFileName){
	fileName = newFileName;
}

string Logic::executeCommand(string userCommand){

	string commandTypeString = extractCommand(userCommand);
	string restOfCommand = extractRestOfUserCommand(userCommand);
	COMMAND_TYPE commandType = determineCommandType(commandTypeString);
	ICommand *cmd;

	switch (commandType){
	case Logic::CREATE_TASK:
		try{
			cmd = new CreateCommand(restOfCommand);
		} catch (string error_message)
		{
			return error_message;
		}
		history.InsertUndoRedo(cmd);
		return cmd->Execute();

	case Logic::DISPLAY_TASK:
		cmd = new DisplayCommand(restOfCommand);
		return cmd->Execute();

	case Logic::UPDATE_TASK:
		try{
			cmd = new UpdateCommand(restOfCommand);
		} catch(string error_message)
		{
			return error_message;
		}
		history.InsertUndoRedo(cmd);
		return cmd->Execute();

	case Logic::DELETE_TASK:
		try{
			cmd = new DeleteCommand(restOfCommand);
		} catch(string error_message)
		{
			return error_message;
		}
		history.InsertUndoRedo(cmd);
		return cmd->Execute();

	case Logic::UNDO_TASK:
		try{
			cmd= history.getUndo();
		} catch(string error_message){
			return error_message;
		}
		return cmd->Unexecute();

	case Logic::REDO_TASK:
		try{
			cmd= history.getRedo();
		}catch (string error_message){
			return error_message;
		}
		return cmd->Execute();

	case Logic::SEARCH:
		cmd = new SearchCommand(restOfCommand);
		return cmd->Execute();

	case Logic::EXIT:
		return exitProgram();

	default:
		sprintf_s(buffer,CommonConstant::MESSAGE_INVALID_COMMAND.c_str(),userCommand.c_str());
		return buffer;
	}
}
Logic::COMMAND_TYPE Logic::getCommandType(){
	return commandType;
}

void Logic::memoryRelease(){
	storage->~Storage();
}

string Logic::exitProgram(){
	return CommonConstant::MESSAGE_EXIT;
}

int Logic::loadFromFile(){

	fstream infile;

	if(checkFileExists(fileName)){
		infile.open(fileName);
		if(infile.is_open()){

			string taskType,title,description,location,person;
			time_t startTime,endTime,time;
			int priority,finishedStatus,sizeOfTaskList;

			infile >> sizeOfTaskList;
			infile.ignore(1,'\n');

			for(int i=0;i < sizeOfTaskList; i++){
				getline(infile,taskType,';');
				getline(infile,title,';');
				getline(infile,description,';');
				getline(infile,location,';');
				getline(infile,person,';');
				infile >> priority;
				infile.ignore(1,';');
				infile >> finishedStatus;
				int index = storage->size();
				if(taskType == "f"){
					
					FloatingTask* task = new FloatingTask(title,description,location,person,priority,finishedStatus,index);
					storage->addTask(task);
				}

				if(taskType == "d"){
					infile.ignore(1,';');
					infile >> time;
					DeadlineTask* task = new DeadlineTask(title,description,location,person,priority,finishedStatus,index,time);
					storage->addTask(task);
				}
				
				if(taskType == "t"){
					infile.ignore(1,';');
					infile >> startTime;
					infile.ignore(1,';');
					infile >> endTime;
					TimedTask* task = new TimedTask(title,description,location,person,priority,finishedStatus,index,startTime,endTime);
					storage->addTask(task);
				}
				infile.ignore(1,'\n');
			}
			infile.close();
			return 1;

		} else
			return -1;
	}

	return 0;
}

int Logic::writeToFile(){
	fstream outfile;
	outfile.open(fileName,ios::out);

	if(outfile.is_open()){
		outfile << storage->size() << endl;
		for(int i=0; i< storage->size(); i++){
			outfile << storage->getTaskList()[i]->getTaskType() << ';'
					<< storage->getTaskList()[i]->getTitle() << ';'
					<< storage->getTaskList()[i]->getDescription() << ';'
					<< storage->getTaskList()[i]->getLocation() << ';'
					<< storage->getTaskList()[i]->getPerson() << ';'
					<< storage->getTaskList()[i]->getPriority() << ';'
					<< storage->getTaskList()[i]->getFinishedStatus();

			if(storage->getTaskList()[i]->getTaskType() == 'd'){
				outfile << ';';
				outfile << storage->getTaskList()[i]->getTimeAndDate();
			}

			if(storage->getTaskList()[i]->getTaskType() == 't'){
				outfile << ';';
				outfile << storage->getTaskList()[i]->getStartTimeAndDate() << ';';
				outfile << storage->getTaskList()[i]->getEndTimeAndDate();
			}

			if(i < storage->size()-1){
				outfile << endl;
			}
		}
		outfile.close();
		return 1;
	}
	return 0;
} 

int Logic::checkFileExists(string filename){
	ifstream checkfile;
	checkfile.open(filename);

	if(checkfile.is_open()){
		checkfile.close();
		return 1;
	}

	return 0;
}

Logic::COMMAND_TYPE Logic::determineCommandType(string command){
	if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_CREATE))
		return Logic::CREATE_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_DISPLAY))
		return Logic::DISPLAY_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_UPDATE))
		return Logic::UPDATE_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_DELETE))
		return Logic::DELETE_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_UNDO))
		return Logic::UNDO_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_REDO))
		return Logic::REDO_TASK;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_SEARCH))
		return Logic::SEARCH;
	else if(isEqualIgnoreCaseString(command,CommonConstant::COMMAND_EXIT))
		return Logic::EXIT;
	else
		return Logic::INVALID;
}

string Logic::extractCommand(string userCommand){
	istringstream iss(userCommand);
	string command_type;

	iss >> command_type;

	return command_type;
}

string Logic::extractRestOfUserCommand(string userCommand){
	istringstream iss(userCommand);
	string commandType;

	iss >> commandType;
	userCommand.erase(userCommand.begin(),userCommand.begin()+commandType.size());

	return userCommand;
}

string Logic::toLowerCase(string word){
	for(unsigned int i=0;i < word.size();i++){
		if(isalpha(word[i])){
			word[i] = tolower(word[i]);
		}
	}

	return word;
}

bool Logic::isEqualIgnoreCaseString(string str1, string str2){
	str1 = toLowerCase(str1);
	str2 = toLowerCase(str2);

	if(str1 == str2){
		return true;
	}

	return false;
}